package no.eirikb.bomberman.shared;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Hashtable;
import no.eirikb.bomberman.applet.game.Bomb;
import no.eirikb.bomberman.applet.game.BoomBrick;
import no.eirikb.bomberman.applet.game.Brick;
import no.eirikb.bomberman.applet.game.Fire;
import no.eirikb.bomberman.applet.game.PowerUp;

/**
 *
 * @author eirikb
 */
public class Game implements Serializable {

    private String name;
    private Hashtable<String, User> users;
    private int max;
    private boolean showNicks;
    private Map[][] map;
    private int size; // Taking over for SIZE!
    private int startSpeed;
    private int startBombs;
    private int startBombSize;
    private int startBombTime;
    private ArrayList<Bomb> bombs;
    private ArrayList<Fire> fires;
    private ArrayList<Brick> bricks;
    private ArrayList<BoomBrick> boomBricks;
    private ArrayList<PowerUp> powerUps;

    /**
     * Contructor with only the variables that MUST be set
     * The rest (like size) is set to defaults!
     * @param name Name of the game
     * @param max Max users that could join the game
     * @param width The width of the game (in squares)
     * @param height The height of the game (in squares)
     */
    public Game(String name, int max, int width, int height) {
        this.name = name;
        this.max = max;
        size = 32;
        startSpeed = 1;
        startBombs = 1;
        startBombSize = 1;
        startBombTime = 3;
        bombs = new ArrayList<Bomb>();
        fires = new ArrayList<Fire>();
        bricks = new ArrayList<Brick>();
        boomBricks = new ArrayList<BoomBrick>();
        powerUps = new ArrayList<PowerUp>();


        users = new Hashtable<String, User>();
        width = width % 2 == 0 ? width + 1 : width;
        height = height % 2 == 0 ? height + 1 : height;
        map = new Map[height][width];
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                map[x][y] = Map.BOX.UNKNOWN;
            }
        }
        for (int y = 1; y < height - 1; y += 2) {
            for (int x = 1; x < width - 1; x += 2) {
                map[x][y] = Map.BOX;
            }
        }
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                if (Map.canWalk(map[x][y]) && Math.random() > 0.6) {
                    map[x][y] = Map.BRICK;
                    bricks.add(new Brick(x, y));
                }
            }
        }
    }

    public void addUser(User user) {
        users.put(user.getNick(), user);
    }

    public void removeUser(User user) {
        users.remove(user.getNick());
    }

    public void updateUser(User user) {
        users.put(user.getNick(), user);
    }

    public String getName() {
        return name;
    }

    public Hashtable<String, User> getUsers() {
        return users;
    }

    public int getMax() {
        return max;
    }

    public int getReady() {
        int tot = 0;
        for (User user : users.values()) {
            if (user.isReady()) {
                tot++;
            }
        }
        return tot;
    }

    public boolean isReady() {
        int ready = getReady();
        return ready > 1 && ready == max;
    }

    /**
     * Get the number of squares in width
     * @return Squares in with
     */
    public int getWidth() {
        return map.length;
    }

    /**
     * Get the number of squares in height
     * @return Number of squares in height
     */
    public int getHeight() {
        return map[0].length;
    }

    public boolean isShowNicks() {
        return showNicks;
    }

    public void setShowNicks(boolean showNicks) {
        this.showNicks = showNicks;
    }

    public int getSize() {
        return size;
    }

    public void setSize(int size) {
        this.size = size;
    }

    public Map[][] getMap() {
        return map;
    }

    public void setMap(Map[][] map) {
        this.map = map;
    }

    public int getStartSpeed() {
        return startSpeed;
    }

    public void setStartSpeed(int startSpeed) {
        this.startSpeed = startSpeed;
    }

    public void addBomb(Bomb bomb) {
        bombs.add(bomb);
        map[bomb.getX()][bomb.getY()] = Map.BOMB;
    }

    public void removeFire(Fire fire) {
        fires.remove(fire);
    }

    public int getStartBombs() {
        return startBombs;
    }

    public void setStartBombs(int startBombs) {
        this.startBombs = startBombs;
    }

    public int getStartBombSize() {
        return startBombSize;
    }

    public void setStartBombSize(int startBombSize) {
        this.startBombSize = startBombSize;
    }

    public int getStartBombTime() {
        return startBombTime;
    }

    public void setStartBombTime(int startBombTime) {
        this.startBombTime = startBombTime;
    }

    public void addPowerUp(PowerUp powerUp) {
        powerUps.add(powerUp);
    }

    public void addFire(Fire fire) {
        fires.add(fire);
        User[] us = users.values().toArray(new User[0]);
        for (User u : us) {
            int x = (int) (u.getPos().getX() / size);
            int y = (int) (u.getPos().getY() / size);
            if (fire.getX() == x && fire.getY() == y) {
                removeUser(u);
            }
        }
    }

    public ArrayList<Bomb> getBombs() {
        return bombs;
    }

    public ArrayList<Fire> getFires() {
        return fires;
    }

    public ArrayList<Brick> getBricks() {
        return bricks;
    }

    public ArrayList<BoomBrick> getBoomBricks() {
        return boomBricks;
    }

    public ArrayList<PowerUp> getPowerUps() {
        return powerUps;
    }

    public void removeBomb(Bomb bomb) {
        Bomb b2 = null;
        for (Bomb b : bombs) {
            if (bomb.compareTo(b) == 0) {
                b2 = b;
            }
        }
        if (b2 != null) {
            map[b2.getX()][b2.getY()] = Map.UNKNOWN;
            bombs.remove(b2);
        }
    }

    public void removeBrick(int x, int y) {
        for (int i = 0; i < bricks.size(); i++) {
            Brick b = bricks.get(i);
            if (b.getX() == x && b.getY() == y) {
                bricks.remove(b);
                boomBricks.add(new BoomBrick(x, y));
                break;
            }
        }
    }

    public void removeBoomBrick(int x, int y) {
        for (int i = 0; i < bricks.size(); i++) {
            BoomBrick b = boomBricks.get(i);
            if (b.getX() == x && b.getY() == y) {
                boomBricks.remove(b);
                map[x][y] = Map.UNKNOWN;
                break;
            }
        }
    }

    public void removePowerUp(int x, int y) {
        for (int i = 0; i < powerUps.size(); i++) {
            PowerUp p = powerUps.get(i);
            if (p.getX() == x && p.getY() == y) {
                powerUps.remove(p);
                break;
            }
        }
    }

    /**
     * Get the Map of the current x-y-location
     * X as in whole square (16 X 16 px big minimum)
     * @param x The x-position
     * @param y The y-position
     * @return Map of the given square
     */
    public Map getMap(int x, int y) {
        if (x >= 0 && x < map.length &&
                y >= 0 && y < map[0].length) {
            return map[x][y];
        } else {
            return Map.UNKNOWN;
        }
    }

    /**
     * Check if x is inside the map and y is inside the map
     * @param x x-coordinate of the user
     * @param y y-coordinate of the user
     * @return true if the user is inside the map
     */
    public boolean canMove(double x, double y) {
        return (x >= 0 && x + size <= map.length * size &&
                y >= 0 && y + size <= map[0].length * size);
    }

    /**
     * Check if the next square is walkable
     * @param x1 X-square
     * @param y2 Y-square
     * @return true if the next square is walkable
     */
    public boolean canMoveMap(int x1, int y1) {
        if (x1 >= 0 && x1 < map.length && y1 >= 0 && y1 < map[0].length) {
            return (Map.canWalk(map[x1][y1]));
        } else {
            return false;
        }
    }

    /**
     * Check if normal x and y-coordinates of a square can move
     * @param x X-coordinate
     * @param y y-Coordinate
     * @return true if there is no obstacle within the square, false if there is
     */
    public boolean canMoveMapSquare(double x, double y) {
        int xs1 = (int) (x / size);
        int ys1 = (int) (y / size);
        int xs2 = (int) ((x - 1) / size + 1);
        int ys2 = (int) ((y - 1) / size + 1);
        return (Map.canWalk(map[xs1][ys1]) &&
                Map.canWalk(map[xs1][ys2]) &&
                Map.canWalk(map[xs2][ys1]) &&
                Map.canWalk(map[xs2][ys2]));
    }

    public PowerUp getpowerUp(int x, int y) {
        PowerUp[] ps = powerUps.toArray(new PowerUp[0]);
        for (PowerUp p : ps) {
            if (p.getX() == x && p.getY() == y) {
                return p;
            }
        }
        return null;
    }

    @Override
    public String toString() {
        return "[" + users.size() + "/" + max + "] " + name;
    }
}
